package wechat

import (
	"bytes"
	"crypto/md5"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"time"

	"jgt-wallet/src/gopay/util"
)

type AppConfig struct {
	SellerID string `xml:"seller_id"` // 商户号ID
	AppID    string `xml:"app_id"`    // 应用ID
	Key      string `xml:"key"`       // 应用密钥
}

type AppClient struct {
	Name  string //名称
	AppID string // 公众账号ID
	MchID string // 商户号ID
	Key   string // 密钥
}

// NewAppClient 创建新微信APP支付
func NewAppClient(key string, conf *AppConfig) (*AppClient, error) {
	var (
		client = new(AppClient)
		err    error
	)
	if conf.SellerID == "" ||
		conf.AppID == "" ||
		conf.Key == "" {
		return nil, errors.New("缺少必要参数")

	}

	client.MchID = conf.SellerID
	client.AppID = conf.AppID
	client.Key = conf.Key

	return client, err
}

func (a *AppClient) Pay(charge *Charge) (map[string]string, error) {
	var (
		m     = make(map[string]string)
		sign  string
		xmlRe *WeChatQueryResult
		err   error
	)

	m["appid"] = a.AppID
	m["mch_id"] = a.MchID
	m["nonce_str"] = util.RandomStr()
	m["body"] = util.TruncatedText(charge.Describe, 32)
	m["out_trade_no"] = charge.TradeNum
	m["total_fee"] = util.MoneyFeeToFenString(charge.MoneyFee)
	m["spbill_create_ip"] = util.LocalIP()
	m["notify_url"] = charge.CallbackURL
	m["trade_type"] = "APP"
	m["sign_type"] = "MD5"

	sign, err = a.GenSign(a.Key, m)
	if err != nil {
		return nil, errors.New("WechatApp.sign: " + err.Error())
	}

	m["sign"] = sign

	if a.Name == "test" {
		xmlRe, err = PostWechat("https://api.mch.weixin.qq.com/sandbox/pay/unifiedorder", m, nil)
	} else {
		xmlRe, err = PostWechat("https://api.mch.weixin.qq.com/pay/unifiedorder", m, nil)
	}

	if err != nil {
		return nil, err
	}

	var c = make(map[string]string)
	c["appid"] = a.AppID
	c["partnerid"] = a.MchID
	c["prepayid"] = xmlRe.PrepayID
	c["package"] = "Sign=WXPay"
	c["noncestr"] = util.RandomStr()
	c["timestamp"] = fmt.Sprintf("%d", time.Now().Unix())

	sign2, err := a.GenSign(a.Key, c)
	if err != nil {
		return nil, errors.New("WechatApp.paySign: " + err.Error())
	}
	c["paySign"] = strings.ToUpper(sign2)

	return c, nil
}

func (a *AppClient) PayToClient(charge *Charge) (map[string]string, error) {
	return map[string]string{}, errors.New("暂未开发该功能")
}

// 订单查询
func (a *AppClient) QueryOrder(tradeNum string) (*WeChatQueryResult, error) {
	var m = make(map[string]string)
	m["appid"] = a.AppID
	m["mch_id"] = a.MchID
	m["out_trade_no"] = tradeNum
	m["nonce_str"] = util.RandomStr()

	sign, err := a.GenSign(a.Key, m)
	if err != nil {
		return nil, err
	}

	m["sign"] = sign

	return PostWechat("https://api.mch.weixin.qq.com/pay/orderquery", m, nil)
}

// GenSign 产生签名
func (a *AppClient) GenSign(key string, m map[string]string) (string, error) {
	var signData []string
	for k, v := range m {
		if v != "" && k != "sign" && k != "key" {
			signData = append(signData, fmt.Sprintf("%s=%s", k, v))
		}
	}

	sort.Strings(signData)
	signStr := strings.Join(signData, "&")
	signStr = signStr + "&key=" + key

	c := md5.New()
	_, err := c.Write([]byte(signStr))
	if err != nil {
		return "", errors.New("WechatGenSign md5.Write: " + err.Error())
	}
	signByte := c.Sum(nil)
	if err != nil {
		return "", errors.New("WechatGenSign md5.Sum: " + err.Error())
	}
	return strings.ToUpper(fmt.Sprintf("%x", signByte)), nil
}

// CallBack
func (a *AppClient) CallBack(w http.ResponseWriter, r *http.Request) (*WeChatPayResult, error) {
	var returnCode = "FAIL"
	var returnMsg = ""
	defer func() {
		formatStr := `<xml><return_code><![CDATA[%s]]></return_code>
                  <return_msg>![CDATA[%s]]</return_msg></xml>`
		returnBody := fmt.Sprintf(formatStr, returnCode, returnMsg)
		w.Write([]byte(returnBody))
	}()
	var reXML WeChatPayResult
	//body := cb.Ctx.Input.RequestBody
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		//log.Error(string(body))
		returnCode = "FAIL"
		returnMsg = "Bodyerror"
		panic(err)
	}
	err = xml.Unmarshal(body, &reXML)
	if err != nil {
		//log.Error(err, string(body))
		returnMsg = "参数错误"
		returnCode = "FAIL"
		panic(err)
	}

	if reXML.ReturnCode != "SUCCESS" {
		//log.Error(reXML)
		returnCode = "FAIL"
		return &reXML, errors.New(reXML.ReturnCode)
	}
	m := util.XmlToMap(body)

	var signData []string
	for k, v := range m {
		if k == "sign" {
			continue
		}
		signData = append(signData, fmt.Sprintf("%v=%v", k, v))
	}

	key := a.Key

	mySign, err := a.GenSign(key, m)
	if err != nil {
		return &reXML, err
	}

	if mySign != m["sign"] {
		panic(errors.New("签名交易错误"))
	}

	returnCode = "SUCCESS"
	return &reXML, nil
}

//对微信下订单或者查订单
func PostWechat(url string, data map[string]string, h *util.HTTPSClient) (*WeChatQueryResult, error) {
	var xmlRe = new(WeChatQueryResult)

	xmlStr := ParseXml(data)

	hc := new(util.HTTPSClient)
	if h != nil {
		hc = h
	} else {
		hc = util.HTTPSC
	}

	re, err := hc.PostData(url, "text/xml:charset=UTF-8", xmlStr)
	if err != nil {
		return xmlRe, errors.New("HTTPSC.PostData: " + err.Error())
	}

	err = xml.Unmarshal(re, &xmlRe)
	if err != nil {
		return xmlRe, errors.New("xml.Unmarshal: " + err.Error())
	}

	if xmlRe.ReturnCode != "SUCCESS" {
		// 通信失败
		return xmlRe, fmt.Errorf("xmlRe.ReturnMsg[%s] ", xmlRe.ReturnMsg)
	}

	if xmlRe.ResultCode != "SUCCESS" {
		// 业务结果失败
		return xmlRe, fmt.Errorf("xmlRe.ErrCodeDes[%s] ", xmlRe.ErrCodeDes)
	}
	return xmlRe, nil
}

//ParseXml 生成XML
func ParseXml(data map[string]string) string {
	buf := bytes.NewBufferString("")

	for k, v := range data {
		buf.WriteString(fmt.Sprintf("<%s><![CDATA[%s]]></%s>", k, v, k))
	}
	return fmt.Sprintf("<xml>%s</xml>", buf.String())
}
